using Digitalmes.Common.Interfaces;
using Digitalmes.Domain.Entities.Mes.Processes;

namespace Digitalmes.Application.Mes.Processes.Routings.Commands;

/// <summary>
/// 工艺路线复制命令。
/// </summary>
/// <remarks>
/// 用新产品去复制一份工艺路线，产线相同。
/// </remarks>
public sealed record CopyRoutingCommand(long FromRoutingId, string ToRoutingCode, string ToRoutingName, long ToProductId) : IRequest<IResult<bool>>;

internal sealed class CopyRoutingCommandHandler(IUnitOfWorkManage unitOfWork, IUniqueIdGenerator idGenerator) : IRequestHandler<CopyRoutingCommand, IResult<bool>>
{
    public async Task<IResult<bool>> Handle(CopyRoutingCommand request, CancellationToken cancellationToken)
    {
        var uow = unitOfWork.CreateUow();

        var routingRepo = unitOfWork.GetRepository<ProcRouting>();
        var routing = await routingRepo.GetByIdAsync(request.FromRoutingId, cancellationToken);
        if (routing == null)
        {
            return Result.Failure("要复制的工序路线不存在");
        }

        if (routing.RoutingCode == request.ToRoutingCode)
        {
            return Result.Failure("工序路线编号已存在");
        }

        // 检查产品在此工艺路线是否唯一
        // 目前设定：同一产品在同一生产线中必须唯一（启用状态）
        if (await routingRepo.IsAnyAsync(m => m.LineId == routing.LineId && m.ProductId == request.ToProductId, cancellationToken))
        {
            return Result.Failure("该产品在此生产线中已存在可用的工艺路线");
        }

        // 新增 工艺路线；
        routing.Id = idGenerator.NextId();
        routing.RoutingCode = request.ToRoutingCode;
        routing.RoutingName = request.ToRoutingName;
        routing.ProductId = request.ToProductId;
        routing.Order += 10;
        await routingRepo.InsertAsync(routing, cancellationToken);

        // 查找工艺步骤；
        var stepRepo = unitOfWork.GetRepository<ProcRoutingStep>();
        var steps = await stepRepo.GetListAsync(m => m.RoutingId == routing.Id, cancellationToken);
        if (steps.Count > 0)
        {
            var mapStep = steps.Select(s => s.Id).ToDictionary(k => k, v => idGenerator.NextId()); // old/new id

            // 新增 工艺步骤；
            steps.ForEach(s =>
            {
                s.Id = mapStep[s.Id];
                s.RoutingId = routing.Id;
            });
            await stepRepo.InsertRangeAsync(steps, cancellationToken);

            // 新增 工步；
            var stepTaskRepo = unitOfWork.GetRepository<ProcStepTask>();
            var stepTasks = await stepTaskRepo.GetListAsync(m => mapStep.Keys.Contains(m.StepId), cancellationToken);
            var mapStepTask = stepTasks.Select(s => s.Id).ToDictionary(k => k, v => idGenerator.NextId()); // old/new id
            if (stepTasks.Count > 0)
            {
                stepTasks.ForEach(s =>
                {
                    s.Id = mapStepTask[s.Id];
                    s.StepId = mapStep[s.StepId];
                });
                await stepTaskRepo.InsertRangeAsync(stepTasks, cancellationToken);
            }

            // 新增 工序/工步标准；
            var standardRepo = unitOfWork.GetRepository<ProcStepStandard>();
            var standards = await standardRepo.GetListAsync(m => mapStep.Keys.Contains(m.StepId), cancellationToken);
            if (standards.Count > 0)
            {
                standards.ForEach(s =>
                {
                    s.Id = idGenerator.NextId();
                    s.StepId = mapStep[s.StepId];
                    if (s.StepTaskId > 0)
                    {
                        s.StepTaskId = mapStepTask[s.StepId];
                    }
                });
                await standardRepo.InsertRangeAsync(standards, cancellationToken);
            }

            // 新增 工序物料；
            var materialRepo = unitOfWork.GetRepository<ProcStepMaterial>();
            var materials = await materialRepo.GetListAsync(m => mapStep.Keys.Contains(m.StepId), cancellationToken);
            if (materials.Count > 0)
            {
                materials.ForEach(s =>
                {
                    s.Id = idGenerator.NextId();
                    s.StepId = mapStep[s.StepId];
                });
                await materialRepo.InsertRangeAsync(materials, cancellationToken);
            }

            // 后续可添加工艺参数
        }

        uow.Commit();

        return Result.From(true);
    }
}
